package com.oj.controller;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.oj.entity.*;
import com.oj.exception.OJException;
import com.oj.mapper.AttendMapper;
import com.oj.mapper.ContestMapper;
import com.oj.mapper.ContestProblemMapper;
import com.oj.service.ContestService;
import com.oj.service.PrivilegeService;
import com.oj.service.RatingService;
import com.oj.service.UserService;
import com.oj.util.CodeMsg;
import com.oj.util.DateUtil;
import com.oj.util.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Slf4j
@Controller
@RequestMapping
public class AttendController {
    @Autowired
    private AttendMapper attendMapper;

    @Autowired
    private ContestService contestService;
    @Autowired
    private ContestMapper contestMapper;

    @Autowired
    private ContestProblemMapper contestProblemMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private RatingService ratingService;
    @Autowired
    private PrivilegeService privilegeService;

    @ResponseBody
    @RequestMapping(value = {"/api/attend/{username}"}, method = RequestMethod.GET)
    public Result<List> userAttends(@PathVariable String username){
        Map param = new HashMap();
        param.put("username", username);
        return Result.success(attendMapper.queryList(param));
    }

    @GetMapping(value = {"/contest/{contestId}/standings",
            "/contest/{contestId}/standings/page/{pageNum}"})
    public ModelAndView standings(@PathVariable String contestId,
                                  @PathVariable(value = "pageNum",required = false)Integer pageNum,
                                  @RequestParam(value = "pageSize",required = false, defaultValue = "50")Integer pageSize,
                                  HttpServletRequest request){
        if (pageNum == null) {
            pageNum = 1;
        }
        Contest contest;
        User u = userService.getUserBySession(request);
        if (u == null || !privilegeService.admin(u.getUsername())) {
            contest = contestService.query(Integer.parseInt(contestId));
        } else {
            contest = contestMapper.queryAdmin(Integer.parseInt(contestId));
        }
        if (contest == null) {
            return null;
        }
        Date dt = new Date();
        if (contest.getStart_time().getTime()>dt.getTime()) {
            contest.setStatus("PENDING");
            contest.setLeftTime(DateUtil.secondToString((contest.getStart_time().getTime()-new Date().getTime())/1000));
            contest.setFriendlyLeftTime(DateUtil.getFriendlyTimeLeft((contest.getStart_time().getTime()- new Date().getTime())/1000));
        } else if (contest.getEnd_time().getTime()>dt.getTime()) {
            contest.setStatus("RUNNING");
            contest.setLeftTime(DateUtil.secondToString((contest.getEnd_time().getTime()-new Date().getTime())/1000));
            contest.setFriendlyLeftTime(DateUtil.getFriendlyTimeLeft((contest.getEnd_time().getTime()-new Date().getTime())/1000));
        } else {
            contest.setStatus("ENDED");
            contest.setLeftTime("00:00:00");
            contest.setFriendlyLeftTime("00:00:00");
        }

        List<ContestProblem> problems = contestProblemMapper.queryByContest(Integer.parseInt(contestId));

        PageHelper.startPage(pageNum,pageSize);
        Page<Attend> attendList = attendMapper.findByPaging(Integer.parseInt(contestId), contest.getType());
        for (Attend a : attendList.subList(0, attendList.size())) {
            a.setUser(userService.query(a.getUsername()));
            Map param = new HashMap();
            param.put("contestId", contest.getContest_id());
            param.put("type", contest.getType());
            param.put("solved", a.getSolved());
            param.put("score", a.getScore());
            param.put("penalty", a.getPenalty());
            a.setRank(attendMapper.getUserRank(param) + 1);
            a.setPenaltyFriendly(DateUtil.penaltyString(a.getPenalty()));
            a.setAcTimeList(new ArrayList<String>());
            a.setAcTimeIntList(new ArrayList<Integer>());
            a.setWrongSubmits(new ArrayList<Integer>());
            for (int i = 1; i <= problems.size(); i++) {
                Integer tInt = a.getACtime(i);
                a.getAcTimeList().add(DateUtil.secondToString(tInt));
                a.getAcTimeIntList().add(tInt);
                a.getWrongSubmits().add(a.getWrongSubmits(i));
            }
        }

        PageInfo<Attend> pageInfo = new PageInfo<>(attendList);
        ModelAndView mv = new ModelAndView();
        mv.addObject("pageInfo", pageInfo);
        mv.addObject("contest", contest);
        mv.addObject("problems", problems);
        mv.setViewName("contest/standings.html");
        return mv;
    }


    @GetMapping(value = {"/contest/{contestId}/registrants",
            "/contest/{contestId}/registrants/page/{pageNum}"})
    public ModelAndView registrants(@PathVariable Integer contestId,
                                  @PathVariable(value = "pageNum",required = false)Integer pageNum,
                                  @RequestParam(value = "pageSize",required = false, defaultValue = "100")Integer pageSize,
                                  HttpServletRequest request){
        if (pageNum == null) {
            pageNum = 1;
        }
        Contest contest;
        User u = userService.getUserBySession(request);
        if (u == null || !privilegeService.admin(u.getUsername())) {
            contest = contestService.query(contestId);
        } else {
            contest = contestMapper.queryAdmin(contestId);
        }
        if (contest == null) {
            return null;
        }

        PageHelper.startPage(pageNum,pageSize);
        Page<Attend> attendList = attendMapper.getRegistrantsByPaging(contestId);
        for (Attend a : attendList.subList(0, attendList.size())) {
            a.setUser(userService.query(a.getUsername()));
            Rating rating = ratingService.query(contestId, a.getUsername());
            if (rating == null) {
                rating = new Rating();
                rating.setUsername(a.getUser().getUsername());
                rating.setNickname(a.getUser().getNickname());
                rating.setDelta(0);
                rating.setRating(a.getUser().getRating());
                rating.setRank(0);
                rating.setContest_id(contestId);
            }
            rating.setRate(a.getUser().getRate());
            a.setRating(rating);
        }
        Collections.sort(attendList.subList(0, attendList.size()), new Comparator<Attend>() {
            public int compare(Attend r1, Attend r2) {
                int diff = (r2.getRating().getRating() - r2.getRating().getDelta()) - (r1.getRating().getRating()- r1.getRating().getDelta());
                if (diff > 0) {
                    return 1;
                }else if (diff < 0) {
                    return -1;
                }
                return 0;
            }
        });
        PageInfo<Attend> pageInfo = new PageInfo<>(attendList);
        ModelAndView mv = new ModelAndView();
        mv.addObject("pageInfo", pageInfo);
        mv.addObject("contest", contest);
        mv.setViewName("contest/registrants.html");
        return mv;
    }

    @GetMapping(value = {"/contest/{contestId}/unregister"})
    public String delete(@PathVariable Integer contestId, HttpServletRequest request){
        Contest contest = contestService.query(contestId);
        if (contest == null) {
            return null;
        }
        User user = userService.getUserBySession(request);
        if (user == null) {
            return null;
        }
        Attend attend = attendMapper.query(contestId, user.getUsername());
        if (attend == null) {
            return null;
        }
        if(contest.getStart_time().before(new Date())){
            log.info(user.getUsername() + " unregister contest " + contestId + " failed, contest has already started.");
            throw new OJException(CodeMsg.CONTEST_UNREG_ERR_STAT.locale());
        }
        try {
            attendMapper.delete(attend.getId());
            log.info(user.getUsername() + " unregister contest " + contestId);
            return "redirect:/contest/" + contestId + "/registrants";
        } catch (Exception e) {
            throw new OJException(CodeMsg.CONTEST_UNREG_ERR.locale());
        }
    }

    @GetMapping(value = {"/contest/{contestId}/register"})
    public String insert(@PathVariable Integer contestId, HttpServletRequest request){
        try {
            Contest contest = contestService.query(contestId);
            if (contest == null) {
                return null;
            }
            String username = request.getSession().getAttribute("session_username").toString();
            if (username == null) {
                return null;
            }
            User user = userService.query(username);
            if (user == null) {
                return null;
            }

            Date nowDate=new Date();
            if(nowDate.getTime() < contest.getStart_reg().getTime()){
                //error = "Registration hasn't started.";
                return "redirect:/contests";
            }
            if(nowDate.getTime() > contest.getEnd_reg().getTime()){
                //error = "You missed the registration time.";
                return "redirect:/contests";
            }
            Attend attend = attendMapper.query(contestId, username);
            if (attend != null) {
                return "redirect:/contest/" + contestId + "/registrants";
            }
            attendMapper.insert(contestId, username);
            return "redirect:/contest/" + contestId + "/registrants";
        } catch (Exception e) {
            return "redirect:/contest/" + contestId + "/registrants";
        }
    }
}